package com.code.google.mdsc.io.xml.impl.factory;

import java.math.BigDecimal;
import java.util.List;

import com.code.google.mdsc.model.Booking;
import com.code.google.mdsc.model.Debtor;
import com.code.google.mdsc.sepa.AccountIdentificationSEPA;
import com.code.google.mdsc.sepa.BranchAndFinancialInstitutionIdentificationSEPA3;
import com.code.google.mdsc.sepa.CashAccountSEPA1;
import com.code.google.mdsc.sepa.ChargeBearerTypeSEPACode;
import com.code.google.mdsc.sepa.DirectDebitTransactionInformationSDD;
import com.code.google.mdsc.sepa.FinancialInstitutionIdentificationSEPA3;
import com.code.google.mdsc.sepa.IdentificationSchemeNameSEPA;
import com.code.google.mdsc.sepa.LocalInstrumentSEPA;
import com.code.google.mdsc.sepa.PartyIdentificationSEPA1;
import com.code.google.mdsc.sepa.PartyIdentificationSEPA3;
import com.code.google.mdsc.sepa.PartyIdentificationSEPA5;
import com.code.google.mdsc.sepa.PartySEPA2;
import com.code.google.mdsc.sepa.PaymentInstructionInformationSDD;
import com.code.google.mdsc.sepa.PaymentMethod2Code;
import com.code.google.mdsc.sepa.PaymentTypeInformationSDD;
import com.code.google.mdsc.sepa.PersonIdentificationSEPA2;
import com.code.google.mdsc.sepa.PostalAddressSEPA;
import com.code.google.mdsc.sepa.RestrictedPersonIdentificationSEPA;
import com.code.google.mdsc.sepa.RestrictedPersonIdentificationSchemeNameSEPA;
import com.code.google.mdsc.sepa.SequenceType1Code;
import com.code.google.mdsc.sepa.ServiceLevelSEPA;

/**
 * Generates the PaymentInformation XML part
 * 
 */
public class PmtInfFactory extends PartialSEPAFactory {

	private static long pmtInfId = 0;

	/**
	 * Starts the generation of the PaymentInformation XML part
	 * 
	 * @param booking
	 * @param numberOfTransactions
	 * @param cashSum
	 * @param currentDebtor
	 * @return the PaymentInstructionInformationSSD
	 */
	public PaymentInstructionInformationSDD generate(Booking booking,
			long numberOfTransactions, BigDecimal cashSum, Debtor currentDebtor) {

		PaymentInstructionInformationSDD generated = OBJECT_FACTORY
				.createPaymentInstructionInformationSDD();

		generated.setPmtInfId("PmtInfId-" + ++pmtInfId); // TODO check if this
															// is correct
		generated.setPmtMtd(PaymentMethod2Code.DD);
		generated.setBtchBookg(true);
		generated.setNbOfTxs("" + numberOfTransactions);
		generated.setPmtTpInf(generatePaymentTypeInformationSDD(currentDebtor));

		generated.setReqdColltnDt(datatypeFactory
				.newXMLGregorianCalendar(booking.getDateOfBooking()));

		generated.setCdtr(generateCreditor(booking));

		generated.setCdtrAcct(generateCreditorAccount(booking));

		generated.setCdtrAgt(generateCreditorAgent(booking));

		generated.setUltmtCdtr(generateUltimateCreditor());

		generated.setChrgBr(ChargeBearerTypeSEPACode.SLEV);

		generated.setCdtrSchmeId(generateCreditorSchemeIdentification(booking));

		List<DirectDebitTransactionInformationSDD> ddtisddList = generated
				.getDrctDbtTxInf();

		ddtisddList.add(new DrctDbtTxInfFactory().generate(currentDebtor));

		return generated;

	}

	private PartyIdentificationSEPA3 generateCreditorSchemeIdentification(
			Booking booking) {
		PartyIdentificationSEPA3 pisep3 = OBJECT_FACTORY
				.createPartyIdentificationSEPA3();
		PartySEPA2 party = OBJECT_FACTORY.createPartySEPA2();
		PersonIdentificationSEPA2 person = OBJECT_FACTORY
				.createPersonIdentificationSEPA2();
		RestrictedPersonIdentificationSEPA restrictedPerson = OBJECT_FACTORY
				.createRestrictedPersonIdentificationSEPA();

		restrictedPerson.setId(booking.getCreditor().getIdentifier());

		RestrictedPersonIdentificationSchemeNameSEPA rpisnsepa = OBJECT_FACTORY
				.createRestrictedPersonIdentificationSchemeNameSEPA();

		rpisnsepa.setPrtry(IdentificationSchemeNameSEPA.SEPA);
		restrictedPerson.setSchmeNm(rpisnsepa);
		person.setOthr(restrictedPerson);
		pisep3.setId(party);
		return pisep3;
	}

	private BranchAndFinancialInstitutionIdentificationSEPA3 generateCreditorAgent(
			Booking booking) {
		BranchAndFinancialInstitutionIdentificationSEPA3 branchAndFinancialInstitutionIdentification = OBJECT_FACTORY
				.createBranchAndFinancialInstitutionIdentificationSEPA3();

		FinancialInstitutionIdentificationSEPA3 financialInstituationIdent = OBJECT_FACTORY
				.createFinancialInstitutionIdentificationSEPA3();

		financialInstituationIdent.setBIC(booking.getCreditor().getBIC());
		branchAndFinancialInstitutionIdentification
				.setFinInstnId(financialInstituationIdent);

		return branchAndFinancialInstitutionIdentification;
	}

	private CashAccountSEPA1 generateCreditorAccount(Booking booking) {
		CashAccountSEPA1 cashAccount = OBJECT_FACTORY.createCashAccountSEPA1();
		// cashAccount.setCcy(ActiveOrHistoricCurrencyCodeEUR.EUR.name());
		AccountIdentificationSEPA accountIdentification = OBJECT_FACTORY
				.createAccountIdentificationSEPA();

		accountIdentification.setIBAN(booking.getCreditor().getIBAN());

		cashAccount.setId(accountIdentification);

		return cashAccount;
	}

	private PartyIdentificationSEPA5 generateCreditor(Booking booking) {
		PartyIdentificationSEPA5 creditor = OBJECT_FACTORY
				.createPartyIdentificationSEPA5();
		creditor.setNm(booking.getCreditor().getName());

		PostalAddressSEPA postalAdress = OBJECT_FACTORY
				.createPostalAddressSEPA();
		postalAdress.setCtry(booking.getCreditor().getCountry());
		// postalAdress.getAdrLine().addAll(booking.getCreditor().getAddress());
		// creditor.setPstlAdr(postalAdress);

		return creditor;
	}

	private PaymentTypeInformationSDD generatePaymentTypeInformationSDD(
			Debtor debtor) {
		PaymentTypeInformationSDD ptisdd = OBJECT_FACTORY
				.createPaymentTypeInformationSDD();

		ServiceLevelSEPA serviceLevel = OBJECT_FACTORY.createServiceLevelSEPA();
		serviceLevel.setCd("SEPA"); // single valid value
		ptisdd.setSvcLvl(serviceLevel);

		LocalInstrumentSEPA localInstrument = OBJECT_FACTORY
				.createLocalInstrumentSEPA();

		localInstrument.setCd("CORE"); // basis lastschrift
		ptisdd.setLclInstrm(localInstrument);

		ptisdd.setSeqTp(SequenceType1Code.valueOf(debtor.getSequenceType()));

		// CategoryPurposeSEPA categoryPurpose = OBJECT_FACTORY
		// .createCategoryPurposeSEPA();

		// categoryPurpose.setCd("Cd"); // optional

		// ptisdd.setCtgyPurp(categoryPurpose);

		return ptisdd;
	}

	private PartyIdentificationSEPA1 generateUltimateCreditor() {
		PartyIdentificationSEPA1 pisepa1 = OBJECT_FACTORY
				.createPartyIdentificationSEPA1();
		// pisepa1.setId(null) do not set this, page 76
		pisepa1.setNm("abweichender Zahlungsempfaenger");

		return pisepa1;
	}

}